﻿using System.Collections.Generic;

using TracedReality.Primitive;
using TracedReality.RayTracing;

namespace TracedReality.SpatialTrees
{
    /// <summary>
    /// An abstract class for a tree based spatial data structure.
    /// </summary>
    public abstract class ShapeTree
    {
        /// <summary>
        /// The BoundingBox for the volume of this node.
        /// </summary>
        protected BoundingBox bound;

        /// <summary>
        /// The depth of this node.
        /// </summary>
        protected int depth;

        /// <summary>
        /// The List of Shapes in this node.
        /// </summary>
        protected List<Shape> shapes;

        public abstract List<Shape> getAllShapes();

        /// <summary>
        /// Gets the BoundingBox of this node.
        /// </summary>
        /// <returns></returns>
        public BoundingBox getBound() { return bound; }

        /// <summary>
        /// Check of the BoundingBox for this node overlaps another BoundingBox.
        /// </summary>
        /// <param name="b">The BoundingBox to check.</param>
        /// <returns>True if they overlap, false otherwise.</returns>
        public bool overlaps(BoundingBox b) { return bound.overlaps(b); }

        /// <summary>
        /// Check if this is a leaf node.
        /// </summary>
        /// <returns>True if this is a leaf node, false otherwise.</returns>
        public abstract bool isLeaf();

        /// <summary>
        /// Get a count of how many Shapes are in this tree.
        /// </summary>
        /// <returns>How many Shapes are held in this tree.</returns>
        public abstract int getCount();

        /// <summary>
        /// Get the height of this tree.
        /// </summary>
        /// <returns>The height of the tree.</returns>
        public abstract int getHeight();

        /// <summary>
        /// Add a Shape to this tree.
        /// </summary>
        /// <param name="s">Shape to add.</param>
        public abstract void add(Shape s);

        /// <summary>
        /// Use this tree to check of a Ray intersects anything before a given point.
        /// </summary>
        /// <param name="ray">Ray to cast</param>
        /// <param name="t">Distance to check against.</param>
        /// <returns>True if the Ray intersects something earlier, false otherwise.</returns>
        public abstract bool intersectAnythingBefore(Ray ray, double t);

        /// <summary>
        /// Use this tree to cast a Ray and update a Hit.
        /// </summary>
        /// <param name="ray">Ray to cast.</param>
        /// <param name="hit">Hit to hold intersection information.</param>
        /// <returns>1 if the Ray intersects a Shape from the outside,
        /// -1 if the Ray intersects a Shape from the inside,
        /// and 0 if no intersection occurs.</returns>
        public abstract int castRay(Ray ray, Hit hit);

        /// <summary>
        /// Split the current node if it is a leaf so that is contains no more than
        /// a given amount as long as it is above a given depth.
        /// </summary>
        /// <param name="maxcap">The maximum capacity of a node.</param>
        /// <param name="maxdep">The maximum depth of a node.</param>
        public abstract void split(int maxcap, int maxdep);

        public abstract void shrink();
    }
}
